package com.example.app.exponential_smoothing_algorithm.triple;

import lombok.Builder;

import java.text.DecimalFormat;
import java.util.*;

/**
* @DESC:
* @author wangzhuangzhuang
* @date 2022-02-23 14:05
*/
public class TripleExponentialSmoothingMethodTest {

	public static void main(String[] args) {
		TripleSmoothingEntity tripleEntity = new TripleSmoothingEntity();
		List<Double> real_data_list = Arrays.asList(225.2,249.9,263.2,293.6,318.9,356.7,363.3,424.2,466.5,582.0,750.0);
		// 如果发展变化趋势比较平稳的观察值，初始值可以直接用第一个数据
		// 如果变化趋势起伏较大，则要用平均值做初始值。（减少初始值对平滑值的影响）
		Double last_single_predict_param = 246.1;
		Double last_second_predict_param = 246.1;
		Double last_triple_predict_param = 246.1;
		int predict_time = 1;
		tripleEntity.setRealDataList(real_data_list);
		tripleEntity.setLastSinglePredictParam(last_single_predict_param);
		tripleEntity.setLastSecondPredictParam(last_second_predict_param);
		tripleEntity.setLastTriplePredictParam(last_triple_predict_param);
		tripleEntity.setPredictTime(predict_time);
		Double smoothingFactor = 0.5;
		tripleExponentialSmoothingMethod(tripleEntity, smoothingFactor);
	}


	public static String tripleExponentialSmoothingMethod(TripleSmoothingEntity tripleSmoothingEntity, Double smoothingFactor){
		/** 三次指数平滑公式：
		 * 注：3^2 代表3的平方,[1-a]^2 代表 1-a 的平方
		 *  St3 = a * St2(二次平滑结果) + (1-a) * S3t-1
		 *  At = 3 * St1 - 3 * St2 + St3
		 *  Bt = a / 2*[(1-a)^2] * [(6-5a)*St1 - 2*(5-4a)*St2 + (4-3a)*St3]
		 *  Ct = a^2 / 2*[(1-a）^2] * [St1 - 2St2 + St3]
		 *  所以可得的预测值计算为:
		 *  ^Yt+T = At + Bt*T + Ct*T^2
		 **/
		// （1）获取实际观察值列表和最后一次的预测值（存在即合理原则 ---汉涛）
		List<Double> realParamList = tripleSmoothingEntity.getRealDataList();
		Double lastSinglePredictParam = tripleSmoothingEntity.getLastSinglePredictParam();
		Double lastSecondPredictParam = tripleSmoothingEntity.getLastSecondPredictParam();
		Double lastTriplePredictParam = tripleSmoothingEntity.getLastTriplePredictParam();
		// 获取总期数计算均方误差
		int totalTimes = realParamList.size();
		// 用于复位的
		Double resizeSecondPredict = tripleSmoothingEntity.getLastSecondPredictParam();
		Double resizeTriplePredict = tripleSmoothingEntity.getLastTriplePredictParam();
		int predictTime = tripleSmoothingEntity.getPredictTime();
		// 定义结果集合类
		List<Double> singleGapList = new ArrayList<>();
		List<Double> secondGapList = new ArrayList<>();
		List<Double> tripleGapList = new ArrayList<>();
		Map<Double,Double> singleGapMap = new HashMap<>();
		Map<Double,Double> secondGapMap = new HashMap<>();
		Map<Double,Double> tripleGapMap = new HashMap<>();
		// 保留一位小数
		DecimalFormat decimalFormat = new DecimalFormat("#.0");
		double yt_T = lastSecondPredictParam;
		double yt3_T = lastTriplePredictParam;

		// （2）平滑值区间 [1~10]/10,先做一次平滑
		for (double a = 1; a < 10; a++) {
			System.out.println("----------------------------"+a/10+"---------------------------");
			System.out.println("----------------------------"+lastSinglePredictParam+"---------------------------");
			System.out.println("----------------------------"+lastSecondPredictParam+"---------------------------");
			System.out.println("----------------------------"+lastTriplePredictParam+"---------------------------");
			for (Double realData: realParamList) {
				double smoothParam = a/10;
				System.out.println("实际值是："
						+ realData+",一次平滑预测值是："
						+lastSinglePredictParam+"，误差为："
						+ decimalFormat.format(Math.abs(realData-lastSinglePredictParam))
						+ "；二次平滑预测值是："+ yt_T +",误差为："
						+ decimalFormat.format(Math.abs(realData-yt_T))
						+ ";三次平滑预测值是：" + yt3_T+",误差为："
						+ decimalFormat.format(Math.abs(realData-yt3_T)));
				// 统计全部的误差
				singleGapList.add(Math.abs(realData-lastSinglePredictParam));
				secondGapList.add(Math.abs(realData-yt_T));
				tripleGapList.add(Math.abs(realData-yt3_T));

				// （3）获得一次平滑预测值
				lastSinglePredictParam = smoothParam * realData + (1-smoothParam)*lastSinglePredictParam;
				// 保留一位小数
				lastSinglePredictParam = Double.valueOf(decimalFormat.format(lastSinglePredictParam));
				System.out.println("一次平滑预测下次为："+lastSinglePredictParam);
				// （4）计算二次平滑值
				lastSecondPredictParam = smoothParam * lastSinglePredictParam + (1-smoothParam)*lastSecondPredictParam;
				double at = 2 * lastSinglePredictParam - lastSecondPredictParam;
				double bt = smoothParam/(1-smoothParam) * (lastSinglePredictParam - lastSecondPredictParam);
				// （4-1）计算出二次平滑预测值
				yt_T = at + bt * predictTime;
				System.out.println("二次平滑为："+lastSecondPredictParam+",下"+predictTime+"期的预测值为："+yt_T);

				// （5）计算三次平滑值
				lastTriplePredictParam = smoothParam * lastSecondPredictParam + (1-smoothParam)*lastTriplePredictParam;
				double at3 = 3*lastSinglePredictParam - 3*lastSecondPredictParam + lastTriplePredictParam;
				// (5-1)根据时间顺序分配权重
				double bt_s0 = 2 * (1-smoothParam)*(1-smoothParam);
				double bt_s1 = smoothParam/bt_s0;
				double bt_s2 = (6-5*smoothParam)*lastSinglePredictParam;
				double bt_s3 = 2*(5-4*smoothParam)*lastSecondPredictParam;
				double bt_s4 = (4-3*smoothParam)*lastTriplePredictParam;
				double bt_s5 = bt_s1 * (bt_s2 - bt_s3 + bt_s4);
				double bt3 = bt_s5;
				double ct_0 = smoothParam * smoothParam / bt_s0;
				double ct_1 = 2*lastSecondPredictParam;
				double ct_2 = lastSinglePredictParam-ct_1+lastTriplePredictParam;
				double ct_3 = ct_0 * ct_2;
				// (5-2)计算三次滑动的预期值
				yt3_T = at3 + bt3 * predictTime + ct_3 * predictTime * predictTime;
				System.out.println("三次平滑为："+lastTriplePredictParam+",下"+predictTime+"期的预测值为："+yt3_T);
			}
			// 计算MES均方误差
			double totalSingleGap = 0.0;
			double totalSecondGap = 0.0;
			double totalTripleGap = 0.0;
			for (Double singleGap: singleGapList) {
				totalSingleGap = totalSingleGap + singleGap * singleGap;
			}
			for (Double secondGap: secondGapList) {
				totalSecondGap = totalSecondGap + secondGap * secondGap;
			}
			for (Double tripleGap: tripleGapList) {
				totalTripleGap = totalTripleGap + tripleGap * tripleGap;
			}
			singleGapMap.put(a, Math.sqrt(totalSingleGap)/totalTimes);
			secondGapMap.put(a, Math.sqrt(totalSecondGap)/totalTimes);
			tripleGapMap.put(a, Math.sqrt(totalTripleGap)/totalTimes);
			// 每更换一个平滑值，预估值都要复位
			lastSinglePredictParam = tripleSmoothingEntity.getLastSinglePredictParam();
			lastSecondPredictParam = tripleSmoothingEntity.getLastSecondPredictParam();
			lastTriplePredictParam = tripleSmoothingEntity.getLastTriplePredictParam();
			yt_T = resizeSecondPredict;
			yt3_T = resizeTriplePredict;
			// 清空当前list装的误差值
			singleGapList.clear();
			secondGapList.clear();
			tripleGapList.clear();
		}
		return singleGapMap.toString()+"----------------------------------"
				+ secondGapMap.toString()+"---------------------------------"
				+ tripleGapMap.toString();
	}

}